<?php
/**
 * Created by JetBrains PhpStorm.
 * User: maggi
 * Date: 25/01/13
 * Time: 1.45
 * To change this template use File | Settings | File Templates.
 */

Zend_Loader::loadClass('Simulations');
Zend_Loader::loadClass('Sites');
Zend_Loader::loadClass('Buildings');
Zend_Loader::loadClass('Zones');
Zend_Loader::loadClass('Materials');
Zend_Loader::loadClass('Constructions');
Zend_Loader::loadClass('Gases');
Zend_Loader::loadClass('Panes');
Zend_Loader::loadClass('Glazing');


class Application_IDFCompiler
{
    protected $simulation;
    protected $site;
    protected $building;
    protected $terrain;
    protected $template;

    public function __construct($sim_id)
    {
        $simulations = new Simulations();
        $simulation = $simulations->find($sim_id);
        if (!$simulation) {
            throw new Exception("Simulation not found");
        }

        $this->simulation = $simulation;
        $this->site = $simulation->offsetGet(0)->findDependentRowset('Sites')->offsetGet(0);
        $this->building = $simulation->offsetGet(0)->findDependentRowset('Buildings')->offsetGet(0);
        $this->zone = $simulation->offsetGet(0)->findDependentRowset('Zones')->offsetGet(0);

        $this->template = file_get_contents(__DIR__ . "/../templates/IDFCompiler/template.idf");
    }

    public function compile() {
        try {
            $this->compileSimulationControl();
            $this->compileSimulationConstructionsAndMaterials();
            $this->compileSimulationGlazingsAndGases();
            $this->compileSimulationDoorsAndWindows();
            $this->compileSimulationZones();
        } catch (Exception $e) {
            echo $e->getMessage();
            echo "<pre>" . $e->getTrace() . "</pre>";
        }
    }

    protected function compileSimulationControl()
    {
        $this->template = str_replace("%geo_north%",
            $this->_defaultParam($this->site->geo_north), $this->template);
        $this->template = str_replace("%geo_terrain%",
            $this->_defaultParam($this->site->geo_terrain), $this->template);
        $this->template = str_replace("%sim_solar_distr%",
            $this->_defaultParam($this->simulation->offsetGet(0)->sim_solar_distr), $this->template);
        $this->template = str_replace("%sim_timestep%",
            $this->_defaultParam($this->simulation->offsetGet(0)->sim_timestep), $this->template);
        $this->template = str_replace("%geo_lat%",
            $this->_defaultParam($this->site->geo_lat), $this->template);
        $this->template = str_replace("%geo_lon%",
            $this->_defaultParam($this->site->geo_lng), $this->template);
        $this->template = str_replace("%geo_timezone%",
            $this->_defaultParam($this->site->tz_timezone), $this->template);
        $this->template = str_replace("%geo_elevation%",
            $this->_defaultParam($this->site->geo_elevation), $this->template);
        $sim_start = explode("/", $this->simulation->offsetGet(0)->sim_start);
        $this->template = str_replace("%sim_start_month%",
            $this->_defaultParam($sim_start[0]), $this->template);
        $this->template = str_replace("%sim_start_day%",
            $this->_defaultParam($sim_start[1]), $this->template);
        $sim_end = explode("/", $this->simulation->offsetGet(0)->sim_end);
        $this->template = str_replace("%sim_end_month%",
            $this->_defaultParam($sim_end[0]), $this->template);
        $this->template = str_replace("%sim_end_day%",
            $this->_defaultParam($sim_end[0]), $this->template);
        $terrain_temps = unserialize($this->site->terrain_temps);
        $this->template = str_replace("%terrain_temp[1]%",
            $this->_defaultParam($terrain_temps[1]), $this->template);
        $this->template = str_replace("%terrain_temp[2]%",
            $this->_defaultParam($terrain_temps[2]), $this->template);
        $this->template = str_replace("%terrain_temp[3]%",
            $this->_defaultParam($terrain_temps[3]), $this->template);
        $this->template = str_replace("%terrain_temp[4]%",
            $this->_defaultParam($terrain_temps[4]), $this->template);
        $this->template = str_replace("%terrain_temp[5]%",
            $this->_defaultParam($terrain_temps[5]), $this->template);
        $this->template = str_replace("%terrain_temp[6]%",
            $this->_defaultParam($terrain_temps[6]), $this->template);
        $this->template = str_replace("%terrain_temp[7]%",
            $this->_defaultParam($terrain_temps[7]), $this->template);
        $this->template = str_replace("%terrain_temp[8]%",
            $this->_defaultParam($terrain_temps[8]), $this->template);
        $this->template = str_replace("%terrain_temp[9]%",
            $this->_defaultParam($terrain_temps[9]), $this->template);
        $this->template = str_replace("%terrain_temp[10]%",
            $this->_defaultParam($terrain_temps[10]), $this->template);
        $this->template = str_replace("%terrain_temp[11]%",
            $this->_defaultParam($terrain_temps[11]), $this->template);
        $this->template = str_replace("%terrain_temp[12]%",
            $this->_defaultParam($terrain_temps[12]), $this->template);
        $this->template = str_replace("%terrain_reflection_sun%",
            $this->_defaultParam($this->site->terrain_reflection_sun), $this->template);
        $this->template = str_replace("%terrain_reflection_snow%",
            $this->_defaultParam($this->site->terrain_reflection_snow), $this->template);
        $this->template = str_replace("%terrain_reflection_snow%",
            $this->_defaultParam($this->site->terrain_reflection_ssnow), $this->template);

        //Zend_Debug::dump($this->template); exit;
    }

    protected function compileSimulationConstructionsAndMaterials()
    {
        $materialTpl = file_get_contents(__DIR__ . "/../templates/IDFCompiler/material.idf");
        $constructionTpl = file_get_contents(__DIR__ . "/../templates/IDFCompiler/construction.idf");
        $simulationMaterials = array();
        $simulationConstructions = array();

        $materials = new Materials();
        $constructions = new Constructions();

        //windows
        $windowData = unserialize($this->zone->windows);
        $windows = array($windowData['N']['window_north_id'], $windowData['E']['window_east_id'], $windowData['S']['window_south_id'],
            $windowData['W']['window_west_id']);
        //doors
        $doorData = unserialize($this->zone->doors);
        $doors = array($doorData['N']['door_north_id'], $doorData['E']['door_east_id'], $doorData['S']['door_south_id'],
            $doorData['W']['door_west_id']);
        //floors
        $floorData = unserialize($this->zone->floors);
        $floors = array($floorData['floor'], $floorData['ceiling']);

        $allConstructions = array_merge($windows, $doors, $floors);

        foreach ($allConstructions as $constructionId) {

            $constructionSelect = $constructions->select(Zend_Db_Table::SELECT_WITH_FROM_PART);
            $constructionSelect->setIntegrityCheck(false);
            $materialSelect = $materials->select(Zend_Db_Table::SELECT_WITH_FROM_PART);
            $materialSelect->setIntegrityCheck(false);

            if (is_null($constructionId) or array_key_exists($constructionId, $simulationConstructions)) {
                continue;
            }

            $construction = $constructions->find($constructionId);
            $constructionSelect
                ->joinLeft(
                'rel_construction_material',
                'construction.id = rel_construction_material.id_construction',
                array(
                    'id_material', 'layer', 'thickness'
                ))
                ->where('construction.id = ?', $constructionId)
                ->order('rel_construction_material.layer')
            ;
            $constructionMaterials = $constructions->fetchAll($constructionSelect);
            $constructionLayers = array();

            foreach ($constructionMaterials as $constructionMaterial) {

                $material = $materials->find($constructionMaterial->id_material);
                $materialKey = $material->offsetGet(0)->id . "_" . $constructionMaterial->thickness * 1000;
                $constructionLayers[] = $materialKey;

                if (array_key_exists(
                    $materialKey, $simulationMaterials)
                ) {
                    continue;
                }

                $material_prop_physical = $material->offsetGet(0)->findDependentRowset('MaterialPropsPhysical');
                $material_prop_thermal = $material->offsetGet(0)->findDependentRowset('MaterialPropsThermal');

                $simulationMaterials[$materialKey] = str_replace(array(
                        '%description%',
                        '%name%',
                        '%roughness%',
                        '%thickness%',
                        '%conductivity%',
                        '%density%',
                        '%capacity%',
                        '%thermal_absrpt%',
                        '%solar_absrpt%',
                        '%visible_absrpt%'
                    ),
                    array(
                        $material->offsetGet(0)->name,
                        $materialKey,
                        $material_prop_physical->offsetGet(0)->roughness,
                        $material->offsetGet(0)->thickness,
                        $material_prop_thermal->offsetExists(0) ? $material_prop_thermal->offsetGet(0)->conductivity : 0,
                        $material_prop_thermal->offsetExists(0) ? $material_prop_thermal->offsetGet(0)->capacity : 0,
                        $material_prop_thermal->offsetExists(0) ? $material_prop_thermal->offsetGet(0)->density : 0,
                        $material_prop_physical->offsetExists(0) ? $material_prop_physical->offsetGet(0)->thermal_absorptance : 0,
                        $material_prop_physical->offsetExists(0) ? $material_prop_physical->offsetGet(0)->solar_absorptance : 0,
                        $material_prop_physical->offsetExists(0) ? $material_prop_physical->offsetGet(0)->visible_absorptance: 0,
                    ),
                    $materialTpl
                );
            } // foreach material

            $simulationConstructions[$constructionId] = str_replace(array(
                    '%description%',
                    '%name%',
                    '%layers%'
                ),
                array(
                    $construction->offsetGet(0)->name,
                    $constructionId,
                    implode("," . PHP_EOL, $constructionLayers) . ";"
                ),
                $constructionTpl
            );
        } // foreach construction

        $this->template = str_replace("%materials%", implode(PHP_EOL.PHP_EOL, $simulationMaterials), $this->template);
        $this->template = str_replace("%constructions%", implode(PHP_EOL.PHP_EOL, $simulationConstructions), $this->template);
    }


    protected function compileSimulationGlazingsAndGases()
    {
        $gasTpl = file_get_contents(__DIR__ . "/../templates/IDFCompiler/gas.idf");
        $glazingTpl = file_get_contents(__DIR__ . "/../templates/IDFCompiler/construction.idf");
        $paneTpl = file_get_contents(__DIR__ . "/../templates/IDFCompiler/pane.idf");
        $simulationGases = array();
        $simulationPanes = array();
        $simulationGlazings = array();

        $gases = new Gases();
        $panes = new Panes();
        $glazings = new Glazing();

        //windows
        $windowData = unserialize($this->zone->windows);
        $windows = array($windowData['N']['window_north_id'], $windowData['E']['window_east_id'], $windowData['S']['window_south_id'],
            $windowData['W']['window_west_id']);

        foreach ($windows as $glazingId) {

            $glazingSelect = $glazings->select(Zend_Db_Table::SELECT_WITH_FROM_PART);
            $glazingSelect->setIntegrityCheck(false);
            $gasSelect = $gases->select(Zend_Db_Table::SELECT_WITH_FROM_PART);
            $gasSelect->setIntegrityCheck(false);
            $paneSelect = $panes->select(Zend_Db_Table::SELECT_WITH_FROM_PART);
            $paneSelect->setIntegrityCheck(false);

            if (is_null($glazingId) or array_key_exists($glazingId, $simulationGlazings)) {
                continue;
            }

            $glazing = $glazings->find($glazingId);
            $glazingSelect
                ->joinLeft(
                'rel_glazing_pane_gas',
                'glazing.id = rel_glazing_pane_gas.id_glazing',
                array(
                    'id_gas', 'id_pane', 'layer', 'thickness'
                ))
                ->where('glazing.id = ?', $glazingId)
                ->order('rel_glazing_pane_gas.layer')
            ;
            $glazingComponents = $glazings->fetchAll($glazingSelect);
            $glazingLayers = array();

            foreach ($glazingComponents as $glazingComponent) {
                
                if (!is_null($glazingComponent->id_gas)) {
                    $gas = $gases->find($glazingComponent->id_gas);
                    $gasKey = $gas->offsetGet(0)->id . "_" . $glazingComponent->thickness * 1000;
                    $glazingLayers[] = $gasKey;
    
                    if (array_key_exists($gasKey, $glazingComponents)) {
                        continue;
                    }
    
                    $simulationGases[$gasKey] = str_replace(array(
                            '%description%',
                            '%name%',
                            '%thickness%'
                        ),
                        array(
                            $gas->offsetGet(0)->name,
                            $gasKey,
                            $gas->offsetGet(0)->thickness
                        ),
                        $gasTpl
                    );

                } else if (!is_null($glazingComponent->id_pane)) {

                    $pane = $panes->find($glazingComponent->id_pane);
                    $paneKey = $pane->offsetGet(0)->id . "_" . $glazingComponent->thickness * 1000;
                    $glazingLayers[] = $paneKey;

                    if (array_key_exists($paneKey, $glazingComponents)) {
                        continue;
                    }

                    $simulationPanes[$paneKey] = str_replace(array(
                            '%description%',
                            '%name%',
                            '%thickness%',
                            '%solar_transmittance%',
                            '%frontside_solar_reflectance%',
                            '%backside_solar_reflectance%',
                            '%visible_transmittance%',
                            '%frontside_visible_reflectance%',
                            '%backside_visible_reflectance%',
                            '%infrared_transmittance%',
                            '%frontside_infrared_emissivity%',
                            '%backside_infrared_emissivity%',
                            '%conductivity%'
                        ),
                        array(
                            $pane->offsetGet(0)->name,
                            $paneKey,
                            $pane->offsetGet(0)->thickness,
                            $this->_defaultParam($pane->offsetGet(0)->solartransmittance, 0),
                            $this->_defaultParam($pane->offsetGet(0)->frontsolarreflectance, 0),
                            $this->_defaultParam($pane->offsetGet(0)->backsolarreflectance, 0),
                            $this->_defaultParam($pane->offsetGet(0)->visibletransmittance, 0),
                            $this->_defaultParam($pane->offsetGet(0)->frontvisiblereflectance, 0),
                            $this->_defaultParam($pane->offsetGet(0)->backvisiblereflectance, 0),
                            $this->_defaultParam($pane->offsetGet(0)->infraredtransmittance, 0),
                            $this->_defaultParam($pane->offsetGet(0)->frontinfraredreflectance, 0),
                            $this->_defaultParam($pane->offsetGet(0)->backinfraredreflectance, 0),
                            $this->_defaultParam($pane->offsetGet(0)->conductivity, 0)
                        ),
                        $paneTpl
                    );
                    
                }// if
            } // foreach

            $simulationGlazings[$glazingId] = str_replace(array(
                    '%description%',
                    '%name%',
                    '%layers%'
                ),
                array(
                    $glazing->offsetGet(0)->name,
                    $glazingId,
                    implode("," . PHP_EOL, $glazingLayers) . ";"
                ),
                $glazingTpl
            );
        } // foreach construction

        $this->template = str_replace("%gases%", implode(PHP_EOL.PHP_EOL, $simulationGases), $this->template);
        $this->template = str_replace("%panes%", implode(PHP_EOL.PHP_EOL, $simulationPanes), $this->template);
        $this->template = str_replace("%glazings%", implode(PHP_EOL.PHP_EOL, $simulationGlazings), $this->template);

    }

    protected function compileSimulationMaterials() {
        $materialTpl =file_get_contents(__DIR__ . "/../templates/IDFCompiler/material.idf");
        $simulationMaterials = array();

        $materials = new Materials();
        $constructions = new Constructions();

        //windows
        $windows = unserialize($this->zone->windows);


        $constructionSelect = $constructions->select(Zend_Db_Table::SELECT_WITH_FROM_PART);
        $constructionSelect->setIntegrityCheck(false);
        $materialSelect = $materials->select(Zend_Db_Table::SELECT_WITH_FROM_PART);
        $materialSelect->setIntegrityCheck(false);

        foreach ($windows as $construction) {
            if (is_null($construction)) {
                continue;
            }
            $constructionSelect
                ->joinLeft(
                'rel_construction_material',
                'construction.id = rel_construction_material.id_construction',
                array(
                    'id_material'
                ))
                ->where('construction.id = ?', $construction)
            ;
            $constructionMaterials = $constructions->fetchAll($constructionSelect);

            foreach ($constructionMaterials as $constructionMaterial) {

                $material = $materials->find($constructionMaterial->id_material);
                if (array_key_exists(
                    $material->offsetGet(0)->id, $simulationMaterials)
                ) {
                    continue;
                }

                $material_prop_physical = $material->offsetGet(0)->findDependentRowset('MaterialPropsPhysical');
                $material_prop_thermal = $material->offsetGet(0)->findDependentRowset('MaterialPropsThermal');

                $simulationMaterials[$material->offsetGet(0)->id] = str_replace(array(
                        '%description%',
                        '%name%',
                        '%roughness%',
                        '%thickness%',
                        '%conductivity%',
                        '%density%',
                        '%capacity%',
                        '%thermal_absrpt%',
                        '%solar_absrpt%',
                        '%visible_absrpt%'
                    ),
                    array(
                        $material->offsetGet(0)->name,
                        $material->offsetGet(0)->id,
                        $material_prop_physical->offsetGet(0)->roughness,
                        $material->offsetGet(0)->thickness,
                        $material_prop_thermal->offsetExists(0) ? $material_prop_thermal->offsetGet(0)->conductivity : 0,
                        $material_prop_thermal->offsetExists(0) ? $material_prop_thermal->offsetGet(0)->capacity : 0,
                        $material_prop_thermal->offsetExists(0) ? $material_prop_thermal->offsetGet(0)->density : 0,
                        $material_prop_physical->offsetExists(0) ? $material_prop_physical->offsetGet(0)->thermal_absorptance : 0,
                        $material_prop_physical->offsetExists(0) ? $material_prop_physical->offsetGet(0)->solar_absorptance : 0,
                        $material_prop_physical->offsetExists(0) ? $material_prop_physical->offsetGet(0)->visible_absorptance: 0,
                    ),
                    $materialTpl
                );
            }
        }

        $this->template = str_replace("%materials%", implode(PHP_EOL.PHP_EOL, $simulationMaterials), $this->template);

        echo "<pre>";
        echo $this->template;
        exit;

    }

    protected function compileSimulationDoorsAndWindows()
    {
        // Windows surface
        $sides = array("E", "N", "W", "S");
        foreach ($sides as $side) {
            $windowsData = unserialize($this->zone->windows);
            $windows = $windowsData[$side];

            if (is_null(current($windows))) {       // e.g. window_north_id
                $this->template = str_replace("%windows_$side%", null, $this->template);
                continue;
            }
            $windows_template = file_get_contents(__DIR__ . "/../templates/IDFCompiler/windows.idf");

            list ($construction, $length, $height, $elev, $num) = array_values($windows);
            $windows_template = str_replace("%windows_bearing%", $side, $windows_template);
            $windows_template = str_replace("%windows_area%",
                $this->_defaultParam($length, 1) * $this->_defaultParam($height, 1), $windows_template);
            $windows_template = str_replace("%construction%", $construction, $windows_template);
            $windows_template = str_replace("%multiplier%", $num, $windows_template);
            $windows_template = str_replace("%windows_vertexes%",
                implode(",".PHP_EOL, $this->computeWindowsVertexes($side, $windows)) . ";",
                $windows_template);

            $this->template = str_replace("%windows_$side%", $windows_template, $this->template);
        }

        // Doors surface
        $sides = array("E", "N", "W", "S");
        foreach ($sides as $side) {
            $doorsData = unserialize($this->zone->doors);
            $doors = $doorsData[$side];

            if (is_null(current($doors))) {     // e.g. door_north_id
                $this->template = str_replace("%doors_$side%", null, $this->template);
                continue;
            }
            $doors_template = file_get_contents(__DIR__ . "/../templates/IDFCompiler/doors.idf");

            list ($construction, $length, $height, $elev, $num) = array_values($doors);
            $offsetX = ($this->_defaultParam($this->zone->geometry_length, 1) - $length * $num) / ($num + 1);

            $doors_template = str_replace("%doors_bearing%", $side, $doors_template);
            $doors_template = str_replace("%doors_area%",
                $this->_defaultParam($length, 1) * $this->_defaultParam($height, 1), $doors_template);
            $doors_template = str_replace("%construction%", $construction, $doors_template);
            $doors_template = str_replace("%multiplier%", $num, $doors_template);
            $doors_template = str_replace("%doors_x_offset%", $offsetX, $doors_template);
            $doors_template = str_replace("%doors_z_offset%", $elev, $doors_template);
            $doors_template = str_replace("%doors_length%", $length, $doors_template);
            $doors_template = str_replace("%doors_height%", $height, $doors_template);

            $this->template = str_replace("%doors_$side%", $doors_template, $this->template);
        }

        // Frame And Divider
        $framesData = unserialize($this->zone->windows);
        $frames = $framesData['frame'];
        $frames_template = file_get_contents(__DIR__ . "/../templates/IDFCompiler/frames.idf");
        $frames_template = str_replace("%frame_width%", $frames['frame_width'], $frames_template);
        $frames_template = str_replace("%frame_outside%", $frames['frame_outside'], $frames_template);
        $frames_template = str_replace("%frame_inside%", $frames['frame_inside'], $frames_template);
        $frames_template = str_replace("%frame_conductance%", $frames['frame_conductance'], $frames_template);
        $frames_template = str_replace("%frame_solar_absrpt%", $frames['frame_solar_absorptance'], $frames_template);
        $frames_template = str_replace("%frame_visible_absrpt%", $frames['frame_visible_absorptance'], $frames_template);
        $frames_template = str_replace("%frame_emissivity%", $frames['frame_emissivity'], $frames_template);
        $frames_template = str_replace("%divider%", $frames['frame_divider'], $frames_template);
        $frames_template = str_replace("%divider_width%", $frames['frame_divider_width'], $frames_template);
        $frames_template = str_replace("%divider_h_num%", $frames['frame_divider_hnum'], $frames_template);
        $frames_template = str_replace("%divider_v_num%", $frames['frame_divider_vnum'], $frames_template);
        $frames_template = str_replace("%divider_outside%", $frames['frame_divider_outside'], $frames_template);
        $frames_template = str_replace("%divider_inside%", $frames['frame_divider_inside'], $frames_template);
        $frames_template = str_replace("%divider_conductance%", $frames['frame_divider_conductance'], $frames_template);
        $frames_template = str_replace("%divider_solar_absrpt%",
            $frames['frame_divider_solar_absorptance'], $frames_template);
        $doors_template = str_replace("%divider_visible_absrpt%",
            $frames['frame_divider_visible_absorptance'], $frames_template);

        $this->template = str_replace("%frame_and_dividers%", $doors_template, $this->template);

        echo "<pre>";
        echo $this->template;
        exit;

    }


    protected function compileSimulationZones()
    {
        $this->template = str_replace("%volume%",
            $this->_defaultParam($this->zone->geometry_length, 1) *
            $this->_defaultParam($this->zone->geometry_depth, 1) *
            $this->_defaultParam($this->zone->geometry_height, 1), $this->template);

        // Floor
        $floorData = unserialize($this->zone->floors);
        $floor_template = file_get_contents(__DIR__ . "/../templates/IDFCompiler/surface.idf");

        $floor_template = str_replace("%surface_bearing%", "Floor", $floor_template);
        $floor_template = str_replace("%surface_area%",
            $this->_defaultParam($this->zone->geometry_length, 1) *
            $this->_defaultParam($this->zone->geometry_depth, 1), $floor_template);
        $floor_template = str_replace("%surface_type%", "Floor", $floor_template);
        $floor_template = str_replace("%construction%", $floorData['floor'] , $floor_template);
        $floor_template = str_replace("%surface_boundary_type%",
            $this->zone->geometry_storey === 'G' ? 'Ground' : 'Surface', $floor_template);
        $floor_template = str_replace("%surface_boundary_object%",
            $this->zone->geometry_storey === 'G' ? '' : 'SingleZone_Surface_Floor' , $floor_template);
        $floor_template = str_replace("%surface_sun%", "NoSun", $floor_template);
        $floor_template = str_replace("%surface_wind%", "NoWind", $floor_template);
        $floor_template = str_replace("%surface_view_factor%",
            $this->zone->geometry_storey === 'G' ? '1' : '0' , $floor_template);
        $floor_template = str_replace("%surface_vertexes%",
            implode(",".PHP_EOL, $this->computeSurfacesVertexes('Floor')) . ";",
            $floor_template);

        $this->template = str_replace("%surface_floor%", $floor_template, $this->template);

        // Ceiling
        $ceilingData = unserialize($this->zone->floors);
        $ceiling_template = file_get_contents(__DIR__ . "/../templates/IDFCompiler/surface.idf");

        $ceiling_template = str_replace("%surface_bearing%", "Ceiling", $ceiling_template);
        $ceiling_template = str_replace("%surface_area%",
            $this->_defaultParam($this->zone->geometry_length, 1) *
                $this->_defaultParam($this->zone->geometry_depth, 1), $ceiling_template);
        $ceiling_template = str_replace("%surface_type%", "Ceiling", $ceiling_template);
        $ceiling_template = str_replace("%construction%", $ceilingData['ceiling'] , $ceiling_template);
        $ceiling_template = str_replace("%surface_boundary_type%",
            $this->zone->geometry_storey === 'R' ? 'Outdoors' : 'Surface', $ceiling_template);
        $ceiling_template = str_replace("%surface_boundary_object%",
            $this->zone->geometry_storey === 'R' ? '' : 'SingleZone_Surface_Ceiling' , $ceiling_template);
        $ceiling_template = str_replace("%surface_sun%",
            $this->zone->geometry_storey === 'R' ? 'SunExposed' : 'NoSun', $ceiling_template);
        $ceiling_template = str_replace("%surface_wind%",
            $this->zone->geometry_storey === 'R' ? 'WindExposed' : 'NoWind', $ceiling_template);
        $ceiling_template = str_replace("%surface_view_factor%", 0, $ceiling_template);
        $ceiling_template = str_replace("%surface_vertexes%",
            implode(",".PHP_EOL, $this->computeSurfacesVertexes('Ceiling')) . ";",
            $ceiling_template);

        $this->template = str_replace("%surface_ceiling%", $ceiling_template, $this->template);

        // Boundaries
        $sides = array("E", "N", "W", "S");
        foreach ($sides as $side) {
            $wallData = unserialize($this->zone->walls);
            $boundaryData = unserialize($this->zone->boundaries);
            $wall = $wallData[$side];
            $boundary = $boundaryData[$side];

            $wall_template = file_get_contents(__DIR__ . "/../templates/IDFCompiler/surface.idf");

            $wall_template = str_replace("%surface_bearing%", $side, $wall_template);
            $wall_template = str_replace("%surface_area%",
                $this->_defaultParam($this->zone->geometry_height, 1) *
                (($side === 'E' || $side === "W")
                ? $this->_defaultParam($this->zone->geometry_depth, 1)
                : $this->_defaultParam($this->zone->geometry_length, 1)), $wall_template);
            $wall_template = str_replace("%surface_type%", "wall", $wall_template);
            $wall_template = str_replace("%construction%", $wall, $wall_template);
            if ($boundary === 'terrain') {
                $wall_template = str_replace("%surface_boundary_type%", 'Ground', $wall_template);
            } elseif ($boundary === 'adiabatic') {
                $wall_template = str_replace("%surface_boundary_type%", 'Surface', $wall_template);
            } else {
                $wall_template = str_replace("%surface_boundary_type%", 'Outdoors', $wall_template);
            }
            $wall_template = str_replace("%surface_boundary_object%",
                $boundary === 'external' || $boundary === 'windowed' ? '' : 'SingleZone_Surface_' . $side,
                $wall_template);
            $wall_template = str_replace("%surface_sun%",
                $boundary === 'external' || $boundary === 'windowed' ? 'SunExposed' : 'NoSun', $wall_template);
            $wall_template = str_replace("%surface_wind%",
                $boundary === 'external' || $boundary === 'windowed' ? 'WindExposed' : 'NoWind', $wall_template);
            $wall_template = str_replace("%surface_view_factor%",
                $boundary === 'windowed' ? 0.5 : 0, $wall_template);
            $wall_template = str_replace("%surface_vertexes%",
                implode(",".PHP_EOL, $this->computeSurfacesVertexes($side)) . ";",
                $wall_template);

            $this->template = str_replace("%surface_$side%", $wall_template, $this->template);
        }

    }

    private function computeSurfacesVertexes($surface)
    {
        $dx = $this->_defaultParam($this->zone->geometry_length, 1);
        $dy = $this->_defaultParam($this->zone->geometry_depth, 1);
        $dz = $this->_defaultParam($this->zone->geometry_height, 1);

        switch ($surface) {
            case 'Ceiling': // ceiling
                $vertexes = array(
                    0, 0, $dz,
                    $dx, 0, $dz,
                    $dx, $dy, $dz,
                    0, $dy, $dz
                );
                break;
            case 'Floor': // floor
                $vertexes = array(
                    $dx, 0, 0,
                    0, 0, 0,
                    0, $dy, 0,
                    $dx, $dy, 0
                );
                break;
            case 'E': // east windows
                $vertexes = array(
                    $dx, 0, 0,
                    $dx, $dy, 0,
                    $dx, $dy, $dz,
                    $dx, 0, $dz
                );
                break;
            case 'N': // north windows
                $vertexes = array(
                    $dx, $dy, 0,
                    0, $dy, 0,
                    0, $dy, $dz,
                    $dx, $dy, $dz
                );
                break;
            case 'W': // west windows
                $vertexes = array(
                    0, $dy, 0,
                    0, 0, 0,
                    0, 0, $dz,
                    0, $dy, $dz
                );
                break;
            case 'S': // south windows
                $vertexes = array(
                    0, 0, 0,
                    $dx, 0, 0,
                    $dx, 0, $dz,
                    0, 0, $dz
                );
                break;
            default:
                throw new UnexpectedValueException();
                break;
        }

        return $vertexes;
    }

    private function computeWindowsVertexes($surface, $windows)
    {
        $dx = $this->_defaultParam($this->zone->geometry_length, 1);
        $dy = $this->_defaultParam($this->zone->geometry_depth, 1);
        $dz = $this->_defaultParam($this->zone->geometry_height, 1);
        
        list (,$len, $h, $elev, $num) = array_values($windows);
        $offsetX = ($dx - $len * $num) / ($num + 1);
        $offsetY = ($dy - $len * $num) / ($num + 1);

        switch ($surface) {
            case 'E': // east windows
                $vertexes = array(
                    $dx, $offsetY, $elev,
                    $dx, $offsetY + $len, $elev,
                    $dx, $offsetY + $len, $elev + $h,
                    $dx, $offsetY, $elev + $h
                );
                break;
            case 'N': // north windows
                $vertexes = array(
                    $dx - $offsetX, $dy, $elev,
                    $dx - ($offsetX + $len), $dy, $elev,
                    $dx - ($offsetX + $len), $dy, $elev + $h,
                    $dx - $offsetX, $dy, $elev + $h
                );
                break;
            case 'W': // west windows
                $vertexes = array(
                    0, $dy - $offsetY, $elev,
                    0, $dy - ($offsetY + $len), $elev,
                    0, $dy - ($offsetY + $len), $elev + $h,
                    0, $dy - $offsetY, $elev + $h
                );
                break;
            case 'S': // south windows
                $vertexes = array(
                    $offsetX, 0, $elev,
                    $offsetX + $len, 0, $elev,
                    $offsetX + $len, 0, $elev + $h,
                    $offsetX, 0, $elev + $h
                );
                break;
            default:
                throw new UnexpectedValueException();
                break;
        }

        return $vertexes;
    }


    private function _defaultParam($param, $default = null)
    {
        return !empty($param) ? $param : $default;
    }



}
